package com.yonyou.pmclouds.plan.rmiimpl;

import com.alibaba.dubbo.config.annotation.Service;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.bp.processor.AuditInfoInsertMultiProcessor;
import com.yonyou.pmclouds.basecom.bp.processor.AuditInfoUpdateProcessor;
import com.yonyou.pmclouds.basecom.bp.template.MultiInsertBpTemplate;
import com.yonyou.pmclouds.basecom.bp.template.SingleUpdateBpTemplate;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.exception.ConcurrentOperationException;
import com.yonyou.pmclouds.basecom.exception.DataModifiedException;
import com.yonyou.pmclouds.basecom.exception.IllegalRequestArgException;
import com.yonyou.pmclouds.basecom.lock.AutoReleaseLockUtil;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.basecom.util.CommonFieldConst;
import com.yonyou.pmclouds.milestone.service.IMilestoneQuery;
import com.yonyou.pmclouds.organization.entity.OrganizationVO;
import com.yonyou.pmclouds.organization.rmiitf.OrganizationQuery;
import com.yonyou.pmclouds.plan.entity.PlanTempletVO;
import com.yonyou.pmclouds.plan.mapper.PlanTempletMapper;
import com.yonyou.pmclouds.plan.rmiitf.PlanTempletService;
import com.yonyou.pmclouds.plantemplate.entity.ScheduleTemplateItemVO;
import com.yonyou.pmclouds.plantemplate.entity.ScheduleTemplateVO;
import com.yonyou.pmclouds.plantemplate.mapper.ScheduleTemplateItemMapper;
import com.yonyou.pmclouds.plantemplate.mapper.ScheduleTemplateMapper;
import com.yonyou.pmclouds.projecttype.rmiitf.ProjectTypeQuery;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;

@Slf4j
@Service(version = ApplicationConsts.APPLICATION_VERSION, interfaceClass = PlanTempletService.class)
public class PlanTempletServiceImpl implements PlanTempletService {
    private static final String LOCK_PREFIX = "plan_";

    private static final String[] UPDATE_ALL_FIELDS = new String[]{CommonFieldConst.SCODE, CommonFieldConst.SNAME,
            PlanTempletVO.MANAGER, PlanTempletVO.RELATED_BILL_TYPE, PlanTempletVO.EXPECT_DURATION,
            PlanTempletVO.TYPE, PlanTempletVO.DEPEND_TEMPLET, PlanTempletVO.CHILD_COUNT,
            PlanTempletVO.PK_MILESTONE, PlanTempletVO.PK_DEPARTMENT};

    private static final int MAX_LEVEL = 20;
    @Autowired
    private PlanTempletMapper mapper;
    @Autowired
    private IMilestoneQuery milestoneQuery;
    @Autowired
    private OrganizationQuery organizationQuery;
    @Autowired
    private ProjectTypeQuery projectTypeQuery;

    @Autowired
    private ScheduleTemplateMapper scheduleTemplateMapper;

    @Autowired
    private ScheduleTemplateItemMapper scheduleTemplateItemMapper;

    @Override
    public PlanTempletVO[] searchPlanTemp(String keyword, String pkTenant) throws BusinessException {
        PlanTempletVO[] templetVOS = mapper.searchPlanTemp(keyword, pkTenant);
        // 设置里程碑名称、单据类型名称、部门、计划类型名称
        translateRefFields(templetVOS);

        return templetVOS;
    }

    @Override
    public PlanTempletVO[] autoSearchPlanTemp(String pkTenant, String pkOrg, String type) throws BusinessException {
        PlanTempletVO[] templetVOS = mapper.autoSearchPlanTemp(pkTenant,pkOrg,type);
        // 设置里程碑名称、单据类型名称、部门、计划类型名称
        translateRefFields(templetVOS);

        return templetVOS;
    }

    @Override
    public PlanTempletVO[] queryPlanByRoot(String rootId) throws BusinessException {
        if (rootId == null) {
            throw new IllegalRequestArgException();
        }

        PlanTempletVO[] result = mapper.queryPlanByRoot(rootId);
        translateRefFields(result);
        return result;
    }

    @Override
    public void deletePlanTemp(String id, String changeFlag) throws BusinessException {
        // lock project
        if (!AutoReleaseLockUtil.addAutoReleaseLock(LOCK_PREFIX + id)) {
            throw new ConcurrentOperationException();
        }
        PlanTempletVO orig = mapper.getById(id);
        if (!changeFlag.equals(orig.getChangeFlag())) {
            throw new DataModifiedException();
        }

        // 查询节点所有子节点
        List<String> children = new ArrayList<>();
        Queue<String> parents = new LinkedList<>();
        parents.add(orig.getPkPlanTemplet());
        children.add(orig.getPkPlanTemplet());
        do {
            String parent = parents.remove();
            String[] pks = mapper.getChildrenPks(parent);
            if (!ArrayUtils.isEmpty(pks)) {
                children.addAll(Arrays.asList(pks));
                Collections.addAll(parents, pks);
            }
        } while (parents.peek() != null);
        String[] childPks = children.toArray(new String[children.size()]);
        // 删除所有子节点
        for (int i = 0; i < children.size(); i += 100) {
            mapper.deletePlanBatch(ArrayUtils.subarray(childPks, i, i + 100));
        }
        // 修改父节点的count
        mapper.deceaseChildCount(orig.getPkParent(), 1);
    }

    private void deleteBatch(String[] deletePkPlan){
        String[] allChildPks = new String[0];
        for(String plan : deletePkPlan){
            // 查询节点所有子节点
            List<String> children = new ArrayList<>();
            Queue<String> parents = new LinkedList<>();
            parents.add(plan);
            children.add(plan);
            do {
                String parent = parents.remove();
                String[] pks = mapper.getChildrenPks(parent);
                if (!ArrayUtils.isEmpty(pks)) {
                    children.addAll(Arrays.asList(pks));
                    Collections.addAll(parents, pks);
                }
            } while (parents.peek() != null);
            String[] childPks = children.toArray(new String[children.size()]);
            allChildPks = ArrayUtils.addAll(allChildPks, childPks);
        }
        // 删除所有子节点
        if(allChildPks.length > 0) mapper.deletePlanBatch(allChildPks);
    }

    @Override
    public void savePlanTemp(PlanTempletVO temp) throws BusinessException {

        //删除计划
        if(temp.getDeletePlan() != null && temp.getDeletePlan().length > 0){
            deleteBatch(temp.getDeletePlan());
        }

        List<PlanTempletVO> insertList = new ArrayList<>();
        List<PlanTempletVO> updateList = new ArrayList<>();
        Map<String, String> codePlanMap = new HashMap<>();

        genplan(null, temp, insertList, updateList, codePlanMap);//递归遍历计划树
        buildDependPlan(insertList, updateList, codePlanMap);//映射前置计划
        if(!insertList.isEmpty()){
            MultiInsertBpTemplate<PlanTempletVO> bp = new MultiInsertBpTemplate<PlanTempletVO>(mapper, false);
            bp.addBeforeProcessor(new AuditInfoInsertMultiProcessor<PlanTempletVO>());
            bp.insert(insertList.toArray(new PlanTempletVO[0]));
        }
        if(!updateList.isEmpty()){
            SingleUpdateBpTemplate<PlanTempletVO> bp = new SingleUpdateBpTemplate<PlanTempletVO>(mapper, UPDATE_ALL_FIELDS);
            bp.addBeforeProcessor(new AuditInfoUpdateProcessor<PlanTempletVO>());
            for(PlanTempletVO vo : updateList){
                bp.update(vo);
            }
        }
    }

    private void genplan(PlanTempletVO parent, PlanTempletVO temp, List<PlanTempletVO> insertList, List<PlanTempletVO> updateList, Map<String, String> codePlanMap) throws BusinessException {
        //非根计划校验必输项
        if(parent != null) checkPlan(temp);
        //确定是否为新增计划
        boolean isNewTree = true;
        if(temp.getPkPlanTemplet() != null){
            isNewTree = false;
        }else{
            String id = UUID.randomUUID().toString();
            temp.setPkPlanTemplet(id);
        }
        //配置计划编码和主键的映射
        codePlanMap.put(temp.getScode(), temp.getPkPlanTemplet());
        //层级设置pk_parent和pk_root
        if(isNewTree){
            if(parent == null) {
                temp.setPkRoot(temp.getPkPlanTemplet());
            }else{
                temp.setPkRoot(parent.getPkRoot());
                temp.setPkParent(parent.getPkPlanTemplet());
                temp.setPkOrg(parent.getPkOrg());
            }
        }

        //设置租户
        temp.setPkTenant(RuntimeEnvironment.getTenantId());
        //设置来源
        temp.setSource("0");
        //设置类型
        if(parent != null) temp.setType(parent.getType());

        if(temp.getChildren()!=null && temp.getChildren().length > 0){
            temp.setChildCount(temp.getChildren().length);
            for(PlanTempletVO vo : temp.getChildren()){
                genplan(temp, vo, insertList, updateList, codePlanMap);
            }
        }else{
            //校验末级节点层级
            planLevelCheck(temp);
            temp.setChildCount(0);
        }
        if(isNewTree){
            insertList.add(temp);
        } else{
            updateList.add(temp);
        }
    }

    private void checkPlan(PlanTempletVO temp) throws BusinessException {
        if(StringUtils.isBlank(temp.getSname())){
            throw new BusinessException("模板名称不能为空");
        } else if(temp.getSname().length() > 50){
            throw new BusinessException("模板名称长度不能超过50字");
        }
    }

    private void planLevelCheck(PlanTempletVO temp) throws BusinessException {
        String code = temp.getScode();
        int level = 1;
        for (int i = 0; i < code.length(); i++) {
            if (code.charAt(i) == '.') {
                level++;
            }
        }
        if (level > MAX_LEVEL) {
            throw new BusinessException("模板超过最大层级限制！");
        }
    }

    private void buildDependPlan(List<PlanTempletVO> insertList, List<PlanTempletVO> updateList, Map<String, String> codePlanMap) {
        for (PlanTempletVO vo : insertList) {
            if (StringUtils.isNotBlank(vo.getDependPlanName()) && codePlanMap != null) {
                vo.setDependTemplet(codePlanMap.get(vo.getDependPlanName()));
            }
        }
        for (PlanTempletVO vo : updateList) {
            if (StringUtils.isNotBlank(vo.getDependPlanName()) && codePlanMap != null) {
                vo.setDependTemplet(codePlanMap.get(vo.getDependPlanName()));
            } else{
                vo.setDependTemplet("");
            }
        }
    }

    /**
     * 翻译参照字段，设置里程碑名称、关联单据类型名称、nc部门名称
     *
     * @param templetVOS
     */
    private void translateRefFields(PlanTempletVO[] templetVOS) throws BusinessException {
        Set<String> pkMilestoneSet = new HashSet<>();
        Set<String> pkOrgSet = new HashSet<>();
        Set<String> typeSet = new HashSet<>();
        for (PlanTempletVO vo : templetVOS) {
            if (StringUtils.isNotEmpty(vo.getPkMilestone()))
                pkMilestoneSet.add(vo.getPkMilestone());

            if (StringUtils.isNotEmpty(vo.getPkOrg()))
                pkOrgSet.add(vo.getPkOrg());

            if (StringUtils.isNotEmpty(vo.getType()))
                typeSet.add(vo.getType());
        }

        // 查询里程碑名称
        Map<String, String> milestoneMap = null;
        if (pkMilestoneSet.size() > 0) {
            milestoneMap = milestoneQuery.queryNameById(pkMilestoneSet.toArray(new String[0]));
        }

        // 查询组织名称
        Map<String, OrganizationVO> organizationNameMap = queryOrganizationName(pkOrgSet);

        // 查询计划类型名称
        Map<String, String> typeNameMap = null;
        if (typeSet.size() > 0) {
            typeNameMap = projectTypeQuery.queryNamesByIds(typeSet.toArray(new String[0]));
        }

        // 设置参照名称
        for (PlanTempletVO vo : templetVOS) {
            if (milestoneMap != null && StringUtils.isNotEmpty(vo.getPkMilestone()))
                vo.setMilestoneName(milestoneMap.get(vo.getPkMilestone()));

            if (StringUtils.isNotEmpty(vo.getPkOrg())) {
                OrganizationVO organizationVO = organizationNameMap.get(vo.getPkOrg());
                vo.setOrgName(organizationVO == null ? null : organizationVO.getSname());
            }

            if (typeNameMap != null && StringUtils.isNotEmpty(vo.getType()))
                vo.setTypeName(typeNameMap.get(vo.getType()));
        }
    }

    /**
     * 查询组织名称
     *
     * @param pkOrgSet
     * @return
     */
    private Map<String, OrganizationVO> queryOrganizationName(Set<String> pkOrgSet) throws BusinessException {
        Map<String, OrganizationVO> result = new HashMap<>();
        if (CollectionUtils.isEmpty(pkOrgSet))
            return result;

        OrganizationVO[] organizationVOs = organizationQuery.queryByIds(pkOrgSet.toArray(new String[0]));
        if (ArrayUtils.isEmpty(organizationVOs))
            return result;

        for (OrganizationVO vo : organizationVOs) {
            result.put(vo.getPkOrganization(), vo);
        }

        return result;
    }


    @Override
    public String addOldPlanToSchedule(String tenantId) throws BusinessException {
        PlanTempletVO[] planTemplateVOS = mapper.searchPlanTemp(null, tenantId);
        if(planTemplateVOS == null || planTemplateVOS.length <= 0){
            return "未查询到需要升级的计划模版！";
        }

        // 将子计划放到父计划中
        Map<String, List<PlanTempletVO>> childMap = genChildMap(planTemplateVOS);
        // 获取根节点计划
        List<PlanTempletVO> result = childMap.get(StringUtils.EMPTY);

        List<ScheduleTemplateVO> insertScheduleList = new ArrayList<>();
        List<ScheduleTemplateItemVO> insertItemVOList = new ArrayList<>();

        for(PlanTempletVO planViewVO : result){
            ScheduleTemplateVO scheduleVO = new ScheduleTemplateVO();
            scheduleVO.setPkScheduleTemplate(planViewVO.getPkPlanTemplet());
            scheduleVO.setCreationtime(planViewVO.getCreationtime());
            scheduleVO.setCreator(planViewVO.getCreator());
            scheduleVO.setPkTenant(planViewVO.getPkTenant());
            scheduleVO.setChildCount(planViewVO.getChildCount());
            scheduleVO.setCode(planViewVO.getScode());
            scheduleVO.setName(planViewVO.getSname());
            scheduleVO.setModifier(planViewVO.getModifier());
            scheduleVO.setModifytime(planViewVO.getModifytime());
            scheduleVO.setSource(planViewVO.getSource());
            scheduleVO.setType(planViewVO.getType());
            scheduleVO.setPkOrg(planViewVO.getPkOrg());
            scheduleVO.setChangeFlag(planViewVO.getChangeFlag());
            scheduleVO.setDr(planViewVO.getDr());

            insertScheduleList.add(scheduleVO);

            PlanTempletVO[] rootPlanArray = planViewVO.getChildren();

            if(rootPlanArray == null || rootPlanArray.length <= 0){
                continue;
            }


            for(PlanTempletVO rootViewVO : rootPlanArray){
                ScheduleTemplateItemVO rootItemVO = getItemVOByPlan(rootViewVO, null, rootViewVO.getPkPlanTemplet());

                insertItemVOList.add(rootItemVO);

                PlanTempletVO[] childPlanArray = rootViewVO.getChildren();
                getInsertItemVOList(rootItemVO, childPlanArray, insertItemVOList);

            }

        }

        String returnResult = "该租户下计划模版共" + planTemplateVOS.length + "条，根计划模版共" + result.size() + "条，";

        if(insertScheduleList.size() > 0){
            returnResult += "成功迁移计划模版共" + insertScheduleList.size() + "条，";
            MultiInsertBpTemplate<ScheduleTemplateVO> insertBpTemplate = new MultiInsertBpTemplate<ScheduleTemplateVO>(scheduleTemplateMapper, false);
            insertBpTemplate.insert(insertScheduleList.toArray(new ScheduleTemplateVO[]{}));
        }

        if(insertItemVOList.size() > 0){
            returnResult += "成功迁移任务项" + insertItemVOList.size() + "条！";
            MultiInsertBpTemplate<ScheduleTemplateItemVO> insertBpTemplate = new MultiInsertBpTemplate<ScheduleTemplateItemVO>(scheduleTemplateItemMapper, false);
            insertBpTemplate.insert(insertItemVOList.toArray(new ScheduleTemplateItemVO[]{}));
        }

        return returnResult;
    }

    private void getInsertItemVOList(ScheduleTemplateItemVO parentItemVO, PlanTempletVO[] childPlanArray,
                                     List<ScheduleTemplateItemVO> insertItemVOList){
        if(childPlanArray == null || childPlanArray.length <= 0){
            return;
        }

        for(PlanTempletVO viewVO : childPlanArray){
            ScheduleTemplateItemVO childItemVO = getItemVOByPlan(viewVO, parentItemVO.getPkScheduleTemplateItem(), parentItemVO.getPkRoot());
            insertItemVOList.add(childItemVO);
            getInsertItemVOList(childItemVO, viewVO.getChildren(), insertItemVOList);
        }
    }

    private ScheduleTemplateItemVO getItemVOByPlan(PlanTempletVO projectPlanViewVO, String pkParent, String pkRoot){
        ScheduleTemplateItemVO itemVO = new ScheduleTemplateItemVO();
        itemVO.setPkScheduleTemplateItem(projectPlanViewVO.getPkPlanTemplet());
        itemVO.setPkScheduleTemplate(projectPlanViewVO.getPkRoot());
        itemVO.setPlanDays(projectPlanViewVO.getExpectDuration() == null ? 0 : projectPlanViewVO.getExpectDuration());
        itemVO.setChildCount(projectPlanViewVO.getChildCount());
        itemVO.setDependPlan(projectPlanViewVO.getDependTemplet());
        itemVO.setName(projectPlanViewVO.getSname());
        itemVO.setCode(projectPlanViewVO.getScode());
        itemVO.setRelatedBillType(projectPlanViewVO.getRelatedBillType());
        itemVO.setPkParent(pkParent);
        itemVO.setPkRoot(pkRoot);
        itemVO.setCreationtime(projectPlanViewVO.getCreationtime());
        itemVO.setCreator(projectPlanViewVO.getCreator());
        itemVO.setModifier(projectPlanViewVO.getModifier());
        itemVO.setModifytime(projectPlanViewVO.getModifytime());
        itemVO.setDr(projectPlanViewVO.getDr());
        itemVO.setChangeFlag(projectPlanViewVO.getChangeFlag());
        itemVO.setPkMilestone(projectPlanViewVO.getPkMilestone());
        itemVO.setPkDepartment(projectPlanViewVO.getPkDepartment());
        itemVO.setManager(projectPlanViewVO.getManager());
        return itemVO;
    }

    public Map<String, List<PlanTempletVO>> genChildMap(PlanTempletVO[] allVOs) {
        // 构造树
        Map<String, List<PlanTempletVO>> childMap = new HashMap<>();
        for (PlanTempletVO vo : allVOs) {
            vo.setKey(vo.getPkPlanTemplet());
            String parent = vo.getPkParent();
            if (StringUtils.isEmpty(parent)) {
                parent = StringUtils.EMPTY;
            }
            List<PlanTempletVO> children = childMap.computeIfAbsent(parent, k -> new ArrayList<>());
            children.add(vo);
        }

        for (PlanTempletVO vo : allVOs) {
            List<PlanTempletVO> children = childMap.get(vo.getPkPlanTemplet());
            if (children != null) {
                vo.setChildren(children.toArray(new PlanTempletVO[0]));
            }
        }
        return childMap;
    }
}
